package nature.nano.util.accepter;

import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.PrintStream;

import nature.nano.component.Strings;
import nature.nano.component.part.SimpleConfigable;
import nature.nano.environment.Config;
import nature.nano.iface.Accepter;
import nature.nano.iface.Props;
import nature.nano.iface.enumer.EnumStringPairCallback;

public class Printer extends SimpleConfigable implements Accepter {

	private PrintStream printer = System.out;
	private String format = Strings.nano;
	private boolean writeToFile = false;

	private int level = 0;
	
	public Printer() {
		super.initialize(Strings.file, Strings.format);
	}
	
	@Override
	public String setProp(String key, String value) {
		if (key.equals(Strings.file)) {
			setOutputFile(value);
		} else if (key.equals(Strings.format)) {
			String f = value.toLowerCase();
			if (f.contains(Strings.xml)) {
				format = Strings.xml;
			} else if (f.contains(Strings.nano)) {
				format = Strings.nano;
			} else if (f.contains(Strings.half)) {
				format = Strings.half;
			}
		}
		return super.setProp(key, value);
	}
	
	@Override
	public void onBegin(String type, Props props) {
		if (format.equals(Strings.xml)) {
			printXmlBegin(type, props);
		} else if (format.equals(Strings.half)) {
			printHalfBegin(type, props);
		} else {
			printNanoBegin(type, props);			
		}
		++level;
	}

	@Override
	public void onContent(String content) {
		printPrefix();
		print(content);
		println();
	}

	@Override
	public void onEnd(String type) {
		--level;
		
		if (format.equals(Strings.xml) || format.equals(Strings.nano)) {
			printEnd(type);
		}
				
		if (level == 0 && writeToFile) {
			printer.close();
		}
	}

	private void setOutputFile(String file) {
		try {
			file = createFileFolder(file);
			printer = new PrintStream(new BufferedOutputStream(new FileOutputStream(file)));
			writeToFile = true;
		} catch (FileNotFoundException e) {
		}
	}

	private String createFileFolder(String file) {
		int i = file.lastIndexOf(Strings.slash);
		int j = file.lastIndexOf(Strings.rslash);
		int k = (i > j) ? i : j;
		String folder = null;
		if (k < 0) {
			folder = Config.current().getPath("temp");
			file = folder + "/" + file;
		} else {
			folder = file.substring(0, k);
		}
		(new File(folder)).mkdirs();
		return file;
	}

	private void printXmlBegin(String type, Props props) {
		printPrefix();
		print(Strings.lt);
		print(type);
		props.enumProp(
			new EnumStringPairCallback() {
				public void onRead(String key, String value) {
					print(Strings.space);
					print(key);
					print(Strings.equal);
					print(Strings.quote);
					print(value);
					print(Strings.quote);
				}
			});
		print(Strings.rt);
		println();		
	}
	
	private void printHalfBegin(final String type, Props props) {
		String same = props.get(type);
		printPrefix();
		print(type);
		print(Strings.colon);
		if (same != null) {
			print(same);			
		}
		println();
		props.enumProp(
			new EnumStringPairCallback() {
				public void onRead(String key, String value) {
					if (key.equals(type)) {
						return;
					}
					printPrefix(1);
					print(Strings.lb);
					print(key);
					print(Strings.rb);
					print(Strings.equal);
					print(Strings.quote);
					print(value);
					print(Strings.quote);
					println();		
				}
			});			
	}

	private void printNanoBegin(String type, Props props) {
		printPrefix();
		print(Strings.lt);
		print(type);
		print(Strings.rt);
		println();		
		props.enumProp(
			new EnumStringPairCallback() {
				public void onRead(String key, String value) {
					printPrefix(1);
					print(Strings.lb);
					print(key);
					print(Strings.rb);
					print(Strings.equal);
					print(Strings.quote);
					print(value);
					print(Strings.quote);
					println();		
				}
			});			
	}
	
	private void printEnd(String type) {
		printPrefix();
		print(Strings.lt);
		print(Strings.slash);
		print(type);
		print(Strings.rt);
		println();
	}
	
	private void printPrefix() {
		printPrefix(0);
	}
	
	private void printPrefix(int addtion) {
		for (int i = 0; i < level + addtion; ++i) {
			print(Strings.tab);
		}
	}
	
	private void println() {
		printer.println();
	}
	
	private void print(String content) {
		printer.print(content);
	}
	
}
